From dd4bd89ef8b516329154f31afbc52c785e09d964 Mon Sep 17 00:00:00 2001 From: robertl Date: Thu, 19 Jun 2008 02:27:39 +0000 Subject: [PATCH] Dustin Johnson contributes igo8 support. git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@3262 f51c46e8-681c-474f-0cfe-069cfd0219fb --- gpsbabel/Makefile.in | 8 +- gpsbabel/igo8.c | 319 ++++++++++++++++++++++++++++++ gpsbabel/msvc/GPSBabel.vcproj | 7 +- gpsbabel/reference/track/igo8.trk | Bin 0 -> 12216 bytes gpsbabel/vecs.c | 17 +- 5 files changed, 342 insertions(+), 9 deletions(-) create mode 100644 gpsbabel/igo8.c create mode 100644 gpsbabel/reference/track/igo8.trk diff --git a/gpsbabel/Makefile.in b/gpsbabel/Makefile.in index ac64a381c..275ce5b33 100644 --- a/gpsbabel/Makefile.in +++ b/gpsbabel/Makefile.in @@ -58,7 +58,8 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o pcx.o cetus.o copilot.o \ yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \ wbt-200.o stmsdf.o gtrnctr.o dmtlog.o raymarine.o alan.o vitovtt.o \ ggv_log.o g7towin.o garmin_gpi.o lmx.o random.o xol.o dg-100.o \ - navilink.o mtk_logger.o ik3d.o osm.o destinator.o exif.o vidaone.o + navilink.o mtk_logger.o ik3d.o osm.o destinator.o exif.o vidaone.o \ + igo8.o FMTS=@FMTS@ @@ -179,7 +180,7 @@ leaktest: dep: make clean && make EXTRA_CFLAGS="-MMD" && cat *.d */*.d > /tmp/dep && rm *.d */*.d - (echo "internal_styles.c: mkstyle.sh " style/*.style ' ./mkstyle.sh > internal_styles.c || (rm -f internal_styles.c ; exit 1)' ) >> /tmp/dep + echo "internal_styles.c: mkstyle.sh style/*.style ; ./mkstyle.sh > internal_styles.c || (rm -f internal_styles.c ; exit 1)' ) >> /tmp/dep echo Edit Makefile.in and bring in /tmp/dep $(WEB)/htmldoc-$(DOCVERSION)/readme.html: FORCE @@ -957,4 +958,5 @@ zlib/trees.o: zlib/trees.c zlib/deflate.h zlib/zutil.h zlib/zlib.h \ zlib/zconf.h zlib/trees.h zlib/uncompr.o: zlib/uncompr.c zlib/zlib.h zlib/zconf.h zlib/zutil.o: zlib/zutil.c zlib/zutil.h zlib/zlib.h zlib/zconf.h -internal_styles.c: mkstyle.sh style/arc.style style/cambridge.style style/csv.style style/cup.style style/custom.style style/dna.style style/fugawi.style style/garmin301.style style/garmin_poi.style style/geonet.style style/gpsdrive.style style/gpsdrivetrack.style style/gpsman.style style/iblue747.style style/kompass_tk.style style/kompass_wp.style style/ktf2.style style/kwf2.style style/mapconverter.style style/mxf.style style/nima.style style/openoffice.style style/s_and_t.style style/saplus.style style/sportsim.style style/tabsep.style style/tomtom_asc.style style/tomtom_itn.style style/xmap.style style/xmap2006.style style/xmapwpt.style ./mkstyle.sh > internal_styles.c || (rm -f internal_styles.c ; exit 1) +internal_styles.c: mkstyle.sh style/arc.style style/cambridge.style style/csv.style style/cup.style style/custom.style style/dna.style style/fugawi.style style/garmin301.style style/garmin_poi.style style/geonet.style style/gpsdrive.style style/gpsdrivetrack.style style/gpsman.style style/iblue747.style style/kompass_tk.style style/kompass_wp.style style/ktf2.style style/kwf2.style style/mapconverter.style style/mxf.style style/nima.style style/openoffice.style style/s_and_t.style style/saplus.style style/sportsim.style style/tabsep.style style/tomtom_asc.style style/tomtom_itn.style style/xmap.style style/xmap2006.style style/xmapwpt.style + ./mkstyle.sh > internal_styles.c || (rm -f internal_styles.c ; exit 1) diff --git a/gpsbabel/igo8.c b/gpsbabel/igo8.c new file mode 100644 index 000000000..5a4c07f6f --- /dev/null +++ b/gpsbabel/igo8.c @@ -0,0 +1,319 @@ +/* + IGO8 Track Format + + Copyright (C) 2008 Dustin Johnson, Dustin@Dustinj.us + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + + +/* + iGo8 (*.trk) Format Overview + + |------------------------------| <--\ + | ID Block (20B) | | + |------------------------------| | + | | | + | | + | | H + | | e + | | a + | Description Block (256B) | d + | | e + | | r + | | + | | | + | | | + | | | + |------------------------------| <--/ + | Information Block (12B) | + |------------------------------| + | Waypoint 1 | + |------------------------------| + | Waypoint 2 | + |------------------------------| + | Waypoint 3 | + |------------------------------| + | ... | + |------------------------------| + + ID Block: defined by igo8_id_block + Description Block: Two null-terminated unicode 2 strings. + The first is the title of the track, + the second is the description. + Information Block: defined by igo8_information_block + Waypoint: defined by igo8_point + +*/ + +#include +#include +#include "defs.h" + +#define TRUE 1 +#define FALSE 0 +#define FLOAT_TO_INT(x) ((int)((x) + ((x)<0?-0.5:0.5))) +#define IGO8_HEADER_SIZE (sizeof(igo8_id_block) + 256) +#define MYNAME "IGO8" + +typedef struct _igo8_id_block +{ + gbuint32 unknown_1; + gbuint32 unknown_2; + gbuint32 unknown_3; + gbuint32 track_number; + gbuint32 unknown_4; +} igo8_id_block, *p_igo8_id_block; + +typedef struct _igo8_information_block +{ + gbuint32 start_time; // In Unix time + gbuint32 zero; // Doesn't appear to serve a purpose + gbuint32 total_file_size; // In bytes +} igo8_information_block, *p_igo8_information_block; + +typedef struct _igo8_point +{ + gbuint32 unix_time; + gbuint32 lon; + gbuint32 lat; +} igo8_point, *p_igo8_point; + +static gbfile *igo8_file_in; +static gbfile *igo8_file_out; +gbuint32 invented_time; +gbuint32 point_count; + +// Sanity check +static void igo8_check_type_sizes() +{ + if (sizeof(igo8_point) != 12) + { + fatal(MYNAME ": igo8_point is %ld bytes instead of the required 12.\n", + sizeof(igo8_point)); + } + + if (sizeof(igo8_information_block) != 12) + { + fatal(MYNAME ": igo8_information_block is %ld bytes instead of the required 12.\n", + sizeof(igo8_information_block)); + } + + if (sizeof(igo8_id_block) != 20) + { + fatal(MYNAME ": igo8_id_block is %ld bytes instead of the required 20.\n", + sizeof(igo8_id_block)); + } +} + +// Reader initialization callback +static void igo8_read_init(const char *fname) +{ + igo8_file_in = gbfopen_le(fname, "rb", MYNAME); + + // Make sure that we are in the environment we expect and require + igo8_check_type_sizes(); + + // Seek past the header and the Information Block + gbfseek(igo8_file_in, IGO8_HEADER_SIZE + sizeof(igo8_information_block), SEEK_SET); +} + +// Reader callback +static void igo8_read(void) +{ + waypoint *wpt_tmp; + route_head *track_head; + igo8_point point; + + track_head = route_head_alloc(); + track_add_head(track_head); + + while (gbfread(&point, sizeof(point), 1, igo8_file_in) > 0) { + wpt_tmp = waypt_new(); + + wpt_tmp->latitude = le_read32(&point.lat) / (double)0x800000; + wpt_tmp->longitude = le_read32(&point.lon) / (double)0x800000; + wpt_tmp->creation_time = le_read32(&point.unix_time); + + track_add_wpt(track_head, wpt_tmp); + } +} + +// Reader close callback +static void igo8_read_deinit(void) +{ + gbfclose(igo8_file_in); +} + +// Writer initialize callback +static void igo8_write_init(const char *fname) +{ + igo8_file_out = gbfopen_le(fname, "wb", MYNAME); + + igo8_check_type_sizes(); + + invented_time = 1; + point_count = 0; +} + +// Writer close callback +static void igo8_write_deinit(void) +{ + unsigned long normalized_file_size; + + // Seek to the start of the third long in the Information Block, this is + // where we will write out the total size of the file. + gbfseek(igo8_file_out, IGO8_HEADER_SIZE + sizeof(gbuint32)*2, SEEK_SET); + + // The total size of the file is the number of points written + Information block + Header + le_write32(&normalized_file_size, sizeof(igo8_point)*(point_count) + sizeof(igo8_information_block) + IGO8_HEADER_SIZE); + + // Write the size + gbfwrite(&normalized_file_size, sizeof(normalized_file_size), 1, igo8_file_out); + + gbfclose(igo8_file_out); +} + +// Write point callback +static void write_igo8_track_point(const waypoint *wpt) +{ + igo8_point point; + + memset(&point, 0, sizeof(point)); + + // iGo8 appears to expect a time, if one isn't provided + // then we shall make our own, where each point is one + // second apart. + if (wpt->creation_time == 0) + { + le_write32(&point.unix_time, invented_time++); + } + else + { + le_write32(&point.unix_time, wpt->creation_time); + } + + // Write the first part of the Information Block, the start time + if (point_count == 0) + { + gbfwrite(&point, sizeof(point), 1, igo8_file_out); + } + + le_write32(&point.lon, FLOAT_TO_INT(wpt->longitude * 0x800000)); + le_write32(&point.lat, FLOAT_TO_INT(wpt->latitude * 0x800000)); + + gbfwrite(&point, sizeof(point), 1, igo8_file_out); + + // Count the number of point printed, we will use this at the end to + // finish filling out the Information Block. + point_count++; +} + +// This is a sort of hacked together ascii-> unicode 2 converter. I have no idea +// if iGo8 even supports real unicode 2, but is does look like it as every ascii +// character is a short with the ascii character as the least significant 7 bits +// +// Please replace this with a much more filled out and correct version if you see +// fit. +unsigned int ascii_to_unicode_2(char* dst, unsigned int dst_max_length, char* src) +{ + unsigned int current_src_position = 0; + unsigned int current_dst_position = 0; + unsigned short current_unicode_char; + + while (src[current_src_position] != '\0' && current_dst_position+4 <= dst_max_length) + { + le_write16(¤t_unicode_char, src[current_src_position]); + *(gbuint16*)&dst[current_dst_position] = current_unicode_char; + current_src_position++; + current_dst_position += 2; + } + + if (src[current_src_position] == '\0' && current_dst_position+2 <= dst_max_length) + { + *(gbuint16*)&dst[current_dst_position] = 0; + current_dst_position += 2; + } + + return current_dst_position; +} + +void write_header() +{ + char header[IGO8_HEADER_SIZE] = {'\0'}; + igo8_id_block tmp_id_block; + p_igo8_id_block id_block = (p_igo8_id_block)header; + gbuint32 current_position = 0; + + tmp_id_block.unknown_1 = 0x0000029B; + tmp_id_block.unknown_2 = 0x000003E7; + tmp_id_block.unknown_3 = 0x00000003; + + // This appears to be a unique number that IDs the track. + // It is mono-incrementing and offset by 2 above the track number. + // e.g. "Track 1" --> track_number = 3 + // XXX - Dustin: This might need to be modified to print a number that is user-provided. + // XXX - Dustin: My guess is that this number is used as the key for the track color, if + // XXX - Dustin: multiple tracks have the same color they will be colored the same, just + // XXX - Dustin: a guess though. + tmp_id_block.track_number = 0x00000010; + tmp_id_block.unknown_4 = 0x00000001; + + // Byte swap out to the header buffer. + le_write32(&id_block->unknown_1, tmp_id_block.unknown_1); + le_write32(&id_block->unknown_2, tmp_id_block.unknown_2); + le_write32(&id_block->unknown_3, tmp_id_block.unknown_3); + le_write32(&id_block->track_number, tmp_id_block.track_number); + le_write32(&id_block->unknown_4, tmp_id_block.unknown_4); + + // Move past the ID block, we have just filled it. + current_position += sizeof(*id_block); + + // Set the title of the track + // XXX - Dustin: This needs to modified to print a title that the user provides. + current_position += ascii_to_unicode_2((char*)(header+current_position), IGO8_HEADER_SIZE - current_position, "Title"); + + // Set the description of the track + // XXX - Dustin: This needs to modified to print a description that the user provides. + current_position += ascii_to_unicode_2((char*)(header+current_position), IGO8_HEADER_SIZE - current_position, "Description"); + + gbfwrite(&header, IGO8_HEADER_SIZE, 1, igo8_file_out); +} + +// Writer callback +static void igo8_write(void) +{ + write_header(); + track_disp_all(NULL, NULL, write_igo8_track_point); +} + +// Callback definitions +ff_vecs_t igo8_vecs = { + ff_type_file, + { ff_cap_none, ff_cap_read | ff_cap_write, ff_cap_none }, + igo8_read_init, + igo8_write_init, + igo8_read_deinit, + igo8_write_deinit, + igo8_read, + igo8_write, + NULL, + NULL, + CET_CHARSET_UTF8, + 1 +}; + + diff --git a/gpsbabel/msvc/GPSBabel.vcproj b/gpsbabel/msvc/GPSBabel.vcproj index ed82fee4e..62cadcca7 100644 --- a/gpsbabel/msvc/GPSBabel.vcproj +++ b/gpsbabel/msvc/GPSBabel.vcproj @@ -1169,7 +1169,12 @@ > + RelativePath="..\igo8.c" + > + + h^-E$?cZC6zlce!Hn z*qVceHU5mdb8Y^dyKx;J$6a|1{(|@Cdi*8#?z-`9$u|jpUPf z05|7vco4VX$$TEK$KUeB+={>BYq$x2&-Zdep2E*^+BTIxDgJ>cb6xo~p2fMw>HJ6W z3|`K;-kDtM%E@C_<5|2W|8?@P#&Ts$C>8s=`IDM5itmI!fZCk|)_-FpBcPx0!W zqpdh?tHoP$`Yq4bHv9g9KaQba6-kwKtYT9y+pLwYrr~fk-wdXsy zKJUPTIrGDg+>bLqbl^jHrFQPb9eFA5%xyUB*@ZiC+OR8UZv0bCr;=wL*o}9Sui(y{ zc{1nSo%iE4c@I92GY9U;2XorfrNVo0=E+>+-h2}08oP4lz_g(o59ey!ozLg$+=I{N z%xU{@=E-9t`ENdeGoGu|AIRzR9RDD`pVPMo^Ie?lI)pQxGjI3g z2RYYuDBs8H@L`;Lk!$J2xnEfq4(HsjtP4jJryq_i&U}6pXPmU=qdDV$bMDP~F1F-j z_$6-3$8w&F?fE#)*lWXmO1=Xh&#%kd^9jZIZC}p#-;w+Ad)$%x^F-d2Pvkk=i3gN? zUp|Syl^@Fkc|4!bC-YRkiU;uzd?%m6-}3`}Do^Hz_%xovkMdy7v-ubg;mlpBKb>=~ z7kDU7=QsEap3QIZu#&&VXL8#7JfFq$`AI&TGxz3P=kNp`$>;I={3@T%@AA8R0e`_C z@`XHuzv7FEzvSUOS3ZtM@bcn|xrR066Zs{)dhtIvYtTpXOF3&$etTK*c)pyoW=-HL zxC!UHS8{Xyny)IJ%vbYz@+o``XKkIy*Oq)1U&mQbr}Fi@Ay48PxHacoH}dA3>$-`z zF8R&emUG@)xC7_6w{lnhhHvBUrT+HfT+1E2pFHiqllS3|`7S<)-{!mdC?3Q2@Nt}b zdM}^AZ}NTIztr5%CvnEW1EuBzevnTs`9pjbza)RSIOFFL9$fNAc@V!Ye~k0nclhyA zpEf+fLrTq)+=pM0KgEY}j{h|8Ry?ve=NiT7pNz?8xEH_7&+@@MnxEqXIQ{TE_bl}< zaE_mv7x|FlmpJ{F@j1HGkL8#7aCzGH3isw`_*G6H#;@^_oHoB+YSNxJ_!xPP^Cln7 z84qu9?#s9QHs`nT7*0QY%KzknoH6_^pUk-zV>$g6zsJLh-{*_@efbA`dC5QIYdLNB zh;Qax%fE^<4}8pb$?xJ%_+Gw&Kjp{xGX9L8=BxR0exZ0Azr`2Jzu-@JIDg6C6o19j zd4&9Hp2uhMc%EN8fq&-Hga6A$D0+=BC$kpIlv7ynY6^Dg98@{{;i&NcSs-?%Ox!;3im za1{T}|JFZ;^I}e)AIwX579YS%c{catW&8_wHqAF8*>vL!)tSE&i=d!Z^CzSQ_ejd!RwUzb9r5ECO?mxamMG_+?+S!Gq?qB%0qcQ z-hxlymi*E+)`*kr=egFk-d}iq?#NGZYwpGO@&??DFX9ck0iVYk@z0KPByY^Ga~Ix( zNAQXP!y0eOyK+tWW?Y-+$v5X!S5F>0gSX(0@(H{p@69jqR=giS$y;+T9>v>me;&iz z^7TB9x8n=>D{jM=^LxBKKfqsbTOL?y+VM`jK;E7^@M7M9+wh;fBd^D`w7&y4xhnd=wt!i!y3GkHf|$QyAd{*w3N-S}kg$(^|krw@1M#@vth;Enh=-jmz%5bna; za{7NS?#LJN-n=(o!(F)x-^|^(H($%$xleHqzK*Yx@5A@=WxOxH!8!hZ{2He{`}57k z2k<34RDK|z#AoqAJe;rMgZW{;kq_Y~_%80rw{gbUp?o{P!-w%b{1x}&8+kk*&VBhC zK7t4H3_g<6htv2d?$1;B=u$tGd-JiS{un-zXUdP|J$V5i$2;@i+=n}GwSo5Y+>Yz; z3A`0IZq zCh^O*5JdmgH zx7?p+@^=;fp8LsX$)}V&p2~gX@AD6QEWg6j_&9!!r&Z{)IQ-hj<}x z#iRLG-i^oeZ`_tA@*>VYVG{q&U3nfa=EHdgNBY+?yNnGCqN~=HuvtBi@Jq;_RJz^54ZraaH^Ou30B?HQrdwXmH=R4aPoV7Ulnia0c zE9LnPR-gaodr_<3$nYVz%_A+J)C?{AGbd(M}*G5^G=S(~$Wx{I6ekDN9%EqRW= z4u2fG>N9yO&R%gE zZ&OjTE$6&H%C{??!EHEg%QxZeIqgqPTTcIc&+RyS#V@!$XHPheci^u$d#xQg`@QeD z17}Z|_Uy#z|HZsB=QuU&33uTMT#q|)_J_%LYsm-lhrBZ%#P4w@KA4~4J@}9c_vE|}?k_*I zd3QdU2gu_=oc7<(r*OWdT*;^M$$TB3R_ZV3!8}Nw_v;}gzl2Zc^v^{+ zl+(7n-<`p^-V=Bj=a?t*nS3JWeef(!o0Ff-`MxoL&nbEC#kqW%Jnwqvan7|DpI_XK zFW{l_-1iGP-yAygMV#+EyYX;7m)r3OzO49SzLvL z`a3IpS8>{TH$ST;@51-++q^s9%Xy9t;rsZXd<5UmV@m!&$q$r2Sn^){5Wk})ewZ`2 zi$d@1K!$_IQ7XRiB@hn1R-co66L`WNT7J^162x93mz ze{K7e4^h)x{u%Gbne#vA9>wFhC$BI6f{*9D`AZ(b$MIKu0cVVT&Dp;VSWukf|IB&5cHm$570x(c$PaVc|100Z85_UxMckbi@$sDJ?swjU zGfozBOU}Jm!mT*-`BL7ncp10ne)8qqsnq|$ojKnv|6THU1@BdA{^YKlW3J@Ai&t@X z9w7g#!hds*l2`5U-}Nyy)wx^AYgD*qagJZB!mIKB&Q-g@t5?+2;jGIi`)%C{ufhAv z(_|Y&KjM(afR3Btg~4+n^d?dpI_?N;jCG` z)vU|c^U>Ul@8#@UnsfF%nQvS0D9*Ffk~9Cut@!2Q^^3DdYRzw!-)_KpE;3Fw)tTo$~ zJbSb4N`54_;e3xx&Gwx4k+>~q9ZCPR<9XbZ+wi6X?ob@jA z`CgoN$&81+OMS+0SI)cS2<}$$v$;FxyvK77&iiSOxew>~nLqdCe23qb_v5UmnSb`@ z)j4y{0i`B$`+S=ah;#!~vNKmW$L_JN$eO8RXOXYA$LPvMNKo%qyJzbl`{ z8CO|f2J=$R^$sb{xlZTh@?7IkUddSp&)~EzHD_|ZC!|l#DtXR(HmA*-@j0A!Zp7!7 z`uIHlQ=Wa%`Nf;_1)O`Aee#8zdzZE8B5uIBzr#8G)`mxL`ZoE+oc>S!C8Z|$KREve zl(p|tPCv(&asCZr6TY1DZ(i{gob#q_R~B!~SC#tYSC{(a*Hrl0QnQ8px(eUG8CU6( z8!LPh=e%jp%@w|d(}wiJtrfnF8<+g{3g5x$ll0-86~2qJFHN7{&3PW1^F5rgQHSs4 z%qO+^KF)ow#rJdWWB%Re0nWY7-|#%h*$@AlA1XC}@WaK~qdvl!$MSbRk5>3G&bg95 zUg0Nrp*;K8CpqIY$9bxF89!a>Q!}!{qc~$MHP001x6g9McG~t_aoYTR@d|!{bB+01 zqZc`C$lvX}#F?LSy`wqLUHmd<4zJQbuW+s<|F-oi|L=NVzjrum+*JN2zs31h^Dbw7Onb(1*4bRwd;Aio z4e#@4&OQBrvqtA$e^~0{kN8FT>ijRhm8<=JeI){0Qf_Gup3gbvT+UcZ%{XF)c_UuN-8g%* z<=pvtd(qQ<8|T^Fi2vmGZkRlF-68hde8!FLBd_AFH@Qdr7vIl64)#5q&*S-AwWDtl zyojsuU%aLI>fH2Z^E}t!`SMG-CfAgI%(b`$x6p>w_&|P@Yx8~Fe5mi?+~^kjE3U&Q z@~K>xZ{rcX2KT